#ifndef __BASE_INIFILE_H__
#define __BASE_INIFILE_H__
#pragma once

#include <vector>
#include <algorithm>
#include <string>

#define RET_OK 0
// 没有找到匹配的]
#define ERR_UNMATCHED_BRACKETS 2
// 段为空
#define ERR_SECTION_EMPTY 3
// 段名已经存在
#define ERR_SECTION_ALREADY_EXISTS 4
// 解析key value失败
#define ERR_PARSE_KEY_VALUE_FAILED 5
// 打开文件失败
#define ERR_OPEN_FILE_FAILED 6
// 内存不足
#define ERR_NO_ENOUGH_MEMORY 7
// 没有找到对应的key
#define ERR_NOT_FOUND_KEY 8
// 没有找到对应的section
#define ERR_NOT_FOUND_SECTION 9
// 异常值
#define ERR_EXEC_VALUE 10

namespace butils {
namespace ini
{

const char delim[] = "\n";
struct item {
    std::string key;
    std::string value;
    std::string comment;  // 每个键的注释，都是指该行上方的内容
    std::string commentr;
};

struct section {
	auto begin() {
        return items.begin();  // 段结构体的begin元素指向items的头指针
    }

	auto end() {
        return items.end();  // 段结构体的begin元素指向items的尾指针
    }

    std::string name;
    std::string comment;  // 每个段的注释，都是指该行上方的内容
    std::string commentr;
    std::vector<item> items;  // 键值项数组，一个段可以有多个键值，所有用vector来储存
};

class file
{
public:
	explicit file();
	~file();

public:
	/* 打开并解析一个名为fname的INI文件 */
	int load(const std::string &fname);
	/* 将内容保存到当前文件 */
	int save();
	/* 将内容另存到一个名为fname的文件 */
	int save_as(const std::string &fname);
	
	/* 获取section段第一个键为key的string值，成功返回0，否则返回错误码 */
	int get_string_value(const std::string &sec, const std::string &key, std::string *value);
	/* 获取section段第一个键为key的int值，成功返回0，否则返回错误码 */
	int get_int_value(const std::string &sec, const std::string &key, int *value);
	/* 获取section段第一个键为key的double值，成功返回0，否则返回错误码 */
	int get_double_value(const std::string &sec, const std::string &key, double *value);
	/* 获取section段第一个键为key的bool值，成功返回0，否则返回错误码 */
	int get_bool_value(const std::string &sec, const std::string &key, bool *value);
	/* 获取注释，如果key=""则获取段注释 */
	int string_comment(const std::string &sec, const std::string &key, std::string *comment);
	/* 获取行尾注释，如果key=""则获取段的行尾注释 */
	int string_commentr(const std::string &sec, const std::string &key, std::string *commentr);
	
	/* 获取section段第一个键为key的string值，成功返回获取的值，否则返回默认值 */
	std::string string_value(const std::string &sec, const std::string &key, const std::string &default_value = "");
	/* 获取section段第一个键为key的int值，成功返回获取的值，否则返回默认值 */
	int int_value(const std::string &sec, const std::string &key, int default_value = 0);
	/* 获取section段第一个键为key的double值，成功返回获取的值，否则返回默认值 */
	double double_value(const std::string &sec, const std::string &key, double default_value = 0.0);
	/* 获取section段第一个键为key的bool值，成功返回获取的值，否则返回默认值 */
	bool bool_value(const std::string &sec, const std::string &key, bool default_value = false);
	
	/* 获取section段所有键为key的值,并将值赋到values的vector中 */
	int array_values(const std::string &sec, const std::string &key, std::vector<std::string> *values);	/* 获取section段所有键为key的值,并将值赋到values的vector中,,将注释赋到comments的vector中 */	int array_values(const std::string &sec, const std::string &key, std::vector<std::string> *values, std::vector<std::string> *comments);
	
	/* 获取section列表,并返回section个数 */
	int sections(std::vector<std::string> *sections);
	/* 获取section个数，至少存在一个空section */
	int section_cnt();
	/* 是否存在某个section */
	bool has_section(const std::string &sec);
	/* 获取指定section的所有ken=value信息 */
	section *get_section(const std::string &sec = "");
	
	/* 是否存在某个key */
	bool has_key(const std::string &sec, const std::string &key);
	
	/* 同时设置值和注释 */
	int set_value(const std::string &sec, const std::string &key, const std::string &value, const std::string &comment = "");
	/* 设置字符串值 */
	int set_string_value(const std::string &sec, const std::string &key, const std::string &value);
	/* 设置整形值 */
	int set_int_value(const std::string &sec, const std::string &key, int value);
	/* 设置浮点数值 */
	int set_double_value(const std::string &sec, const std::string &key, double value);
	/* 设置布尔值 */
	int set_bool_value(const std::string &sec, const std::string &key, bool value);
	/* 设置注释，如果key=""则设置段注释 */
	int set_comment(const std::string &sec, const std::string &key, const std::string &comment);
	/* 设置行尾注释，如果key=""则设置段的行尾注释 */
	int set_commentr(const std::string &sec, const std::string &key, const std::string &commentr);
	
	/* 删除段 */
	void delete_section(const std::string &sec);
	/* 删除特定段的特定参数 */
	void delete_key(const std::string &sec, const std::string &key);
	/*设置注释分隔符，默认为"#" */
	void comment_delimiter(const std::string &delimiter);
	/*最新错误消息 */
	const std::string &last_err_msg();
    // for debug
    void print();

private:
	int update_section(const std::string &cline, const std::string &comment,
					   const std::string &commentr, section **sec);
	int add_key_value_pair(const std::string &cline, const std::string &comment,
					   const std::string &commentr, section *sec);
	/* 释放全部资源，清空容器*/
	void release();
	/* 是否是注释 */
	bool is_comment_line(const std::string &str);
	/* 解析Key - Value */
	bool parse(const std::string &content, std::string *key, std::string *value);

	/* 获取section段第一个键为key的值,并将值赋到value中 */
	int get_value(const std::string &sec, const std::string &key, std::string *value);
	/* 获取section段第一个键为key的值,并将值赋到value中,将注释赋到comment中 */
	int get_value(const std::string &sec, const std::string &key, std::string *value, std::string *comment);

private:
	std::vector<section *> sections_vt_;  // 用于存储段集合的vector容器
	std::string file_path_;
	std::string comment_delimiter_;
	std::string err_msg_;  // 保存出错时的错误信息
};

}  // endof namespace inifile
} // endof namespace butils

#endif  // __BASE_INIFILE_H__

